package daytrader.lib;
import daytrader.types.Stock;

public class Indicators {
	private LibDataInterface libData;

	public Indicators(LibDataInterface libData) {
		this.libData = libData;
	}
	
	////////////////////////////////////////////////////////////
	//
	// Naming convention:
	//     Function names are all capital letters 
	//     may or may not have a one-digit numeric suffix
	//
	//     The numeric suffix denotes the number of arguments
	//     (except "end"), with no suffix denoting zero
	//
	//     No two overloaded functions can have the same number
	//     of arguments
	//
	//     Each function must have end as the last argument
	//
	////////////////////////////////////////////////////////////
	
	/**
	 * 	Simple moving average (SMA)
	 *  
	 * @param days	length of the time period (in days)
	 * @param end	the end of the time period (0 for today, positive for history)
	 * @return		MA value
	 */
	public double MA1(int days, int end)
	{
		double sum = 0;
		for (int i = 0; i < days; i++)
			sum += libData.close(i + end);
		
		return sum / days;
	}

	public double MA(int end)
	{
		return MA1(10, end);
	}
		
	/**
	 * 	Exponential moving average (EMA)
	 * 
	 * @param days	length of the time period (in days)
	 * @param end	the end of the time period (0 for today, positive for history)
	 * @return		EMA value
	 */
	public double EMA1(int days, int end)
	{
		double ema = 0;
		for (int i = days - 1; i >= 0; i--)
			ema = (ema * (days - 1) + libData.close(i + end) * 2) / (days + 1);
		
		return ema;
	}
	
	public double EMA(int end)
	{
		return EMA1(10, end);
	}
	
	/**
	 * 	Moving average convergence/divergence (MACD)
	 * 
	 * @param end	the end of the time period (0 for today, positive for history)
	 * @return		MACD value
	 */
	public double MACD(int end)
	{
		return EMA1(12, end) - EMA1(26, end);
	}
	
	/**
	 * 	MACD signal line (i.e. 9-day EMA of MACD)
	 * 
	 * @param end	the end of the time period (0 for today, positive for history)
	 * @return		MACD signal line value
	 */
	public double MACDSL(int end)
	{
		double ema = 0;
		for (int i = 9 - 1; i >= 0; i--)
			ema = (ema * (9 - 1) + MACD(i + end) * 2) / (9 + 1);
		
		return ema;
	}
	
	/**
	 * 	MACD Histogram
	 * 
	 * @param end	the end of the time period (0 for today, positive for history)
	 * @return		MACD Histogram value
	 */
	public double MACDHIST(int end)
	{
		return MACD(end) - MACDSL(end);
	}
	
	/**
	 * 	Relative strength index (RSI)
	 * 
	 * @param end	the end of the time period (0 for today, positive for history)
	 * @return		RSI value
	 */
	public double RSI(int end)
	{
		double ag = 0;	// average gain
		double al = 0;	// average loss
		for (int i = 14 - 1; i >= 0; i--)
		{
			double cn = libData.close(i + end);
			double cp = libData.close(i + end + 1);
			if (cn > cp)	// gain
				ag = (ag * 13 + (cn - cp)) / 14;
			else if (cn < cp)	// loss
				al = (al * 13 + (cp - cn)) / 14;
		}
		if (al == 0)
			return 50;
		else		
			return 100 - 100 / (1 + ag / al);
	}
	
	/**
	 * 	Stochastic oscillator (STO), %K
	 * 
	 * @param days	length of the time period (in days)
	 * @param end	the end of the time period (0 for today, positive for history)
	 * @return		STO value, %K
	 */
	public double STOK1(int days, int end)
	{
		double hh = -1;	// highest high
		double ll = -1;	// lowest low
		for (int i = 0; i < days; i++)
		{
			if (hh < 0 || libData.high(i + end) > hh)
				hh = libData.high(i + end);
			if (ll < 0 || libData.low(i + end) < ll)
				ll = libData.low(i + end);
		}
	
		if (hh - ll == 0)
			return 50;
		else
			return (libData.close(end) - ll) / (hh - ll) * 100;
	}
	
	public double STOK(int end)
	{
		return STOK1(10, end);
	}
	
	/**
	 * 	Stochastic oscillator (STO), %D
	 * 
	 * @param days	length of the time period (in days)
	 * @param end	the end of the time period (0 for today, positive for history)
	 * @return		STO value, %D
	 */
	public double STOD1(int days, int end)
	{
		double sum = 0;
		for (int i = 0; i < 3; i++)
			sum += STOK1(days, end + i);
		
		return sum / 3;
	}
	
	public double STOD(int end)
	{
		return STOD1(10, end);	
	}
	
	/**
	 * 	Stochastic oscillator on RSI (StochRSI)
	 * 
	 * @param days	length of the time period (in days)
	 * @param end	the end of the time period (0 for today, positive for history)
	 * @return		StochRSI value
	 */
	public double STORSI1(int days, int end)
	{
		double hrsi = -1;	// highest of RSI
		double lrsi = -1;	// lowest of RSI
		for (int i = 0; i < days; i++)
		{
			double rsi = RSI(i + end);
			if (hrsi < 0 || rsi > hrsi)
				hrsi = rsi;
			if (lrsi < 0 || rsi < lrsi)
				lrsi = rsi;
		}
	
		if (hrsi == lrsi)
			return 50;
		else
			return (RSI(end) - lrsi) / (hrsi - lrsi) * 100;
	}
	
	public double STORSI(int end)
	{
		return STORSI1(10, end);
	}
	
	/* 
	 * author Ryan
	 * */

	/**
	 * 	Rate of Change (ROC)
	 * 
	 * @param days	length of the time period (in days)
	 * @param end	the end of the time period (0 for today, positive for history)
	 * @return		ROC value
	 *  
	 */
	 public double ROC1(int days, int end) {
		 
		 double nowclose = libData.close(end);
		 double preclose = libData.close(days + end);
		 /*avoid divided by 0 bug*/
		 if (preclose == 0)
			 preclose =0.1;
		 return 100* (nowclose - preclose) / preclose;
	 }
	 
	 public double ROC(int end) {
		 return ROC1(10, end);
	 }
	 
	 
	 /**
	  *  On Balance Volume (OBV)
	  * 
	  * @param days	length of the time period (in days) within which OBV backtracks
	  * @param end	the end of the time period (0 for today, positive for history)
	  * @return		OBV value of @end day backtracking within previous @days days  
	  *  
	  */
	 public double OBV1(int days, int end) {
		 
		 double currvolume = libData.volume(days+end);
		 double prevclose = libData.close(days+end);
		 int date = days+end-1;
		 
		 while (date >= end) {
			 double currclose = libData.close(date);
			 
			 if (currclose > prevclose)
				 currvolume = currvolume + libData.volume(date);
			 else if (currclose < prevclose)
				 currvolume = currvolume - libData.volume(date);
			 
			 prevclose = currclose;
			 --date;
		 }
		 return currvolume;
	 }
	 
	 public double OBV(int end) {
		 return OBV1(10, end);
	 }
	 
	 /**
	  *  Bollinger Middle Band (BMB): 20-day simple moving average (SMA)
	  *  
	  * @param end	the end of the time period (0 for today, positive for history)
	  * @return		BMB value for the "end" day
	  *  
	  */
	 public double BMB(int end) {
		 return MA1(20, end);
	 }
	 
	  /**
	  *  Bollinger Upper Band (BUB): 20-day SMA + (20-day standard deviation of price x 2)
	  *  
	  * @param end	the end of the time period (0 for today, positive for history)
	  * @return		BUB value for the "end" day
	  *  
	  */
	 public double BUB(int end) {
		 double sigma = 0;
		 double avg = MA1(20, end);
		 
		 for (int date = 0; date < 20; date ++ ) {
			 double tmp = libData.close(date+end);
			 sigma += (tmp - avg) * (tmp - avg);
		 }
		 sigma = sigma/20;
		 
		 return MA1(20, end) + 2*Math.sqrt(sigma);
	 }
	  
	 /**
	  *  Bollinger Lower Band (BLB): 20-day SMA - (20-day standard deviation of price x 2)
	  *  
	  * @param end	the end of the time period (0 for today, positive for history)
	  * @return		BLB value for the "end" day
	  *  
	  */
	 public double BLB(int end) {
		 double sigma = 0;
		 double avg = MA1(20, end);
		 
		 for (int date = 0; date < 20; date ++ ) {
			 double tmp = libData.close(date+end);
			 sigma += (tmp - avg) * (tmp - avg);
		 }
		 sigma = sigma/20;
		 
		 return MA1(20, end) - 2*Math.sqrt(sigma);
	 }
	 
	 /**
	  *  Bollinger Band Bandwidth (BBB): (Upper Band - Lower Band)
	  *  
	  * @param end	the end of the time period (0 for today, positive for history)
	  * @return		BBB value for the "end" day
	  *  
	  */
	 public double BBB(int end) {
		 double sigma = 0;
		 double avg = MA1(20, end);
		 
		 for (int date = 0; date < 20; date ++ ) {
			 double tmp = libData.close(date+end);
			 sigma += (tmp - avg) * (tmp - avg);
		 }
		 sigma = sigma/20;
		 
		 return 4*Math.sqrt(sigma);
	 }
	 
	 /**
	  *  Bollinger Band %B (BBPB): 
	  *  			%B = (Price - Lower Band)/(Upper Band - Lower Band))
	  *  
	  * @param end	the end of the time period (0 for today, positive for history)
	  * @return		BBPB value for the "end" day
	  *  
	  */
	 public double BBPB(int end) {
		 double lwb = BLB(end);
		 double upb = BUB(end);
		 double tmp = libData.close(end);
		 
		 if (upb == lwb)
			 return 0.5;
		 else
			 return (tmp - lwb)/(upb-lwb);
	 }
	 
	 /**
	  *  William %R (WPR): 
	  *  			%R = (Highest High - Close)/(Highest High - Lowest Low) * -100
	  *  			The default setting for Williams %R is 14 periods
	  *  
	  * @param end	the end of the time period (0 for today, positive for history)
	  * @return		BBPB value for the "end" day
	  *  
	  */
	 public double WPR(int end) {
		 
		 double hhigh = libData.high(end);
		 double llow = libData.low(end);
		 
		 for (int date = 1; date < 14; date ++) {
			 double high = libData.high(end+date);
			 double low = libData.low(end+date);
			 if (high > hhigh)
				 hhigh = high;
			 if (low < llow)
				 llow = low;
		 }
		 
		 if (hhigh == llow)
			 return -50;
		 else
			 return (hhigh - libData.close(end)) / (hhigh - llow) * -100;
	 }
	 
	 public static void main (String [] argv) {
		 LibData lib = new LibData();
		 boolean ret = lib.openStockFeed(new Stock("foo"));
		 Indicators ind = new Indicators(lib);
		 
		 /* This is only a test main function
		  * Some Indicators are not compatible with current version of Data APIs
		  * Let's discuss and figure out the problem in meetings of tomorrow
		  * 
		  * I have comment out these "not friendly" indicators  :)
		  * */
		 
		 if (ret) {
			 System.out.println("1 "+ind.BBB(5));
			 System.out.println("2 "+ind.BBPB(5));
			 System.out.println("3 "+ind.BLB(5));
			 System.out.println("4 "+ind.BMB(5));
			 System.out.println("5 "+ind.BUB(5));
		 	 System.out.println("6 "+ind.EMA(5));
		 	 System.out.println("7 "+ind.MA(5));
			 System.out.println("8 "+ind.MACD(5));
			 System.out.println("9 "+ind.MACDSL(5));
			 System.out.println("10 "+ind.MACDHIST(5));
			 System.out.println("11 "+ind.OBV(5));
			 System.out.println("12 "+ind.ROC(5));
			 System.out.println("13 "+ind.RSI(5));
			 System.out.println("14 "+ind.STOD(5));
			 System.out.println("15 "+ind.STOK(5));
			 System.out.println("16 "+ind.STORSI(5));
			 System.out.println("17 "+ind.WPR(5));
		 }
	 }
}
